home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Games / Glypha 3v2 / GlyphaIII Code ƒ / Enemy.c next >
Encoding:
Text File  |  1995-06-30  |  44.0 KB  |  1,375 lines  |  [TEXT/CWIE]

  1.  
  2. //============================================================================
  3. //----------------------------------------------------------------------------
  4. //                                    Enemy.c
  5. //----------------------------------------------------------------------------
  6. //============================================================================
  7.  
  8. // This file contains all enemy related functions (enemy "AI").  It handles…
  9. // the enemy decision making proccess, moves the enemies, etc.
  10.  
  11. #include "Externs.h"
  12.  
  13.  
  14. #define kEnemyImpulse            8
  15.  
  16. #define kOwlMaxHVel                96
  17. #define kOwlMaxVVel                320
  18. #define kOwlHeightSmell            96
  19. #define kOwlFlapImpulse            32
  20.  
  21. #define kWolfMaxHVel            128
  22. #define kWolfMaxVVel            400
  23. #define kWolfHeightSmell        160
  24. #define kWolfFlapImpulse        48
  25.  
  26. #define kJackalMaxHVel            192
  27. #define kJackalMaxVVel            512
  28. #define kJackalHeightSmell        240
  29. #define kJackalFlapImpulse        72
  30.  
  31.  
  32. Boolean SetEnemyInitialLocation (Rect *);
  33. void SetEnemyAttributes (short);
  34. short AssignNewAltitude (void);
  35. void InitEnemy (short, Boolean);
  36. void CheckEnemyPlatformHit (short);
  37. void CheckEnemyRoofCollision (short);
  38. void HandleIdleEnemies (short);
  39. void HandleFlyingEnemies (short);
  40. void HandleWalkingEnemy (short);
  41. void HandleSpawningEnemy (short);
  42. void HandleFallingEnemy (short);
  43. void HandleEggEnemy (short);
  44. void ResolveEnemyPlayerHit (short);
  45.  
  46.  
  47. handInfo    theHand;
  48. eyeInfo        theEye;
  49. Rect        grabZone;
  50. short        deadEnemies, spawnedEnemies, numEnemiesThisLevel, numOwls;
  51.  
  52. extern    playerType    thePlayer;
  53. extern    enemyType    theEnemies[kMaxEnemies];
  54. extern    Rect        platformRects[6], enemyInitRects[5];
  55. extern    long        theScore;
  56. extern    short        numLedges, lightningCount, numEnemies, countDownTimer;
  57. extern    short        levelOn, lightH, lightV;
  58. extern    Boolean        evenFrame, doEnemyFlapSound, doEnemyScrapeSound;
  59.  
  60.  
  61. //==============================================================  Functions
  62. //--------------------------------------------------------------  SetEnemyInitialLocation
  63.  
  64. // When a new enemy is about to be "born", this function is called to determine…
  65. // the enemies starting location.  The only thing important here is that the enemy…
  66. // appears on a valid platform for the particular level we're on.  As well, which…
  67. // platform he (it) appears on should be random.
  68.  
  69. Boolean SetEnemyInitialLocation (Rect *theRect)
  70. {
  71.     short        where, possibilities;
  72.     Boolean        facing;
  73.     
  74.     possibilities = numLedges - 1;        // Determine number of valid platforms.
  75.     where = RandomInt(possibilities);    // Choose one at random.
  76.     *theRect = enemyInitRects[where];    // Initially place enemy at default location.
  77.     
  78.     switch (where)                        // Determine if enemy facing left or right.
  79.     {                                    // It depends upon which platform they're on.
  80.         case 0:                            // These are the left-most platforms.
  81.         case 2:
  82.         facing = TRUE;                    // Enemy will face right.
  83.         break;
  84.         
  85.         case 3:                            // Special case for the center platform.
  86.         if (RandomInt(2) == 0)            // Enemy randomly faces either left or right.
  87.             facing = TRUE;
  88.         else
  89.             facing = FALSE;
  90.         break;
  91.         
  92.         default:                        // Catch remaining (right-most) platforms.
  93.         facing = FALSE;                    // Enemy will face left.
  94.         break;
  95.     }
  96.     
  97.     if ((levelOn % 5) == 4)                // Handle special case for Egg Wave
  98.     {                                    // Re-define enemy bounds.
  99.         theRect->left += 12 + RandomInt(48) - 24;
  100.         theRect->right = theRect->left + 24;
  101.         theRect->top = theRect->bottom - 24;
  102.     }
  103.     
  104.     return (facing);
  105. }
  106.  
  107. //--------------------------------------------------------------  SetEnemyAttributes
  108.  
  109. // Depending upon the type of enemy this function is passed (there are three…
  110. // types of sphinx enemies), this function sets up that enemies various…
  111. // attributes - such as maximum vertical velocity, etc.
  112.  
  113. void SetEnemyAttributes (short i)
  114. {
  115.     short        h;
  116.                                             // Point enemy toward center of screen.
  117.     h = (theEnemies[i].dest.left + theEnemies[i].dest.right) >> 1;
  118.     if (h < 320)                            // If enemy in left half of screen…
  119.         theEnemies[i].facingRight = TRUE;    // the enemy will face to the right.
  120.     else                                    // Otherwise, if in right half of screen…
  121.         theEnemies[i].facingRight = FALSE;    // face to the left.
  122.     
  123.     switch (theEnemies[i].kind)                // Okay, depending upon what "kind" of enemy…
  124.     {                                        // we're dealing with....
  125.         case kOwl:                            // The owl is the simplest (wimpiest) enemy.
  126.         if (theEnemies[i].facingRight)        // Choose which graphic to use.
  127.             theEnemies[i].srcNum = 0;
  128.         else
  129.             theEnemies[i].srcNum = 2;
  130.                                             // Set owl's velocity limitations.
  131.         theEnemies[i].maxHVel = kOwlMaxHVel;
  132.         theEnemies[i].maxVVel = kOwlMaxVVel;
  133.                                             // This is the distance within which he will…
  134.                                             // pursue the player (it's strictly Y distance).
  135.         theEnemies[i].heightSmell = kOwlHeightSmell;
  136.                                             // This is how powerful the owl's "flap" is.
  137.         theEnemies[i].flapImpulse = kOwlFlapImpulse;
  138.         break;
  139.         
  140.         case kWolf:                            // The wolf sphinx is of medium difficulty.
  141.         if (theEnemies[i].facingRight)        // Choose which graphic to use.
  142.             theEnemies[i].srcNum = 4;
  143.         else
  144.             theEnemies[i].srcNum = 6;
  145.                                             // Set wolf's velocity limitations.
  146.         theEnemies[i].maxHVel = kWolfMaxHVel;
  147.         theEnemies[i].maxVVel = kWolfMaxVVel;
  148.                                             // This is the distance within which he will…
  149.                                             // pursue the player (it's strictly Y distance).
  150.         theEnemies[i].heightSmell = kWolfHeightSmell;
  151.                                             // This is how powerful the wolf's "flap" is.
  152.         theEnemies[i].flapImpulse = kWolfFlapImpulse;
  153.         break;
  154.         
  155.         case kJackal:                        // The jackal is the swiftest, toughest enemy.
  156.         if (theEnemies[i].facingRight)        // Choose which graphic to use.
  157.             theEnemies[i].srcNum = 8;
  158.         else
  159.             theEnemies[i].srcNum = 10;
  160.                                             // Set jackal's velocity limitations.
  161.         theEnemies[i].maxHVel = kJackalMaxHVel;
  162.         theEnemies[i].maxVVel = kJackalMaxVVel;
  163.                                             // This is the distance within which he will…
  164.                                             // pursue the player (it's strictly Y distance).
  165.         theEnemies[i].heightSmell = kJackalHeightSmell;
  166.                                             // This is how powerful the jackal's "flap" is.
  167.         theEnemies[i].flapImpulse = kJackalFlapImpulse;
  168.         break;
  169.     }
  170. }
  171.  
  172. //--------------------------------------------------------------  AssignNewAltitude
  173.  
  174. // The sphinxes "patrol" specific altitudes in the arena.  After wrapping around…
  175. // the screen a few times, they randomly select a new altitude to patrol (this…
  176. // keeps the player from finding a "safe" place to stand.  This function chooses…
  177. // a new altitude for the enemy to patrol.
  178.  
  179. short AssignNewAltitude (void)
  180. {
  181.     short        which, altitude;
  182.     
  183.     which = RandomInt(4);        // There are only 4 "patrol altitudes".
  184.     switch (which)                // Depending on which random number came up…
  185.     {
  186.         case 0:                    // This is just below the ceiling.
  187.         altitude = 65 << 4;
  188.         break;
  189.         
  190.         case 1:                    // This is below the top platforms but above the…
  191.         altitude = 150 << 4;    // center platform.
  192.         break;
  193.         
  194.         case 2:                    // This is just below the center platform.
  195.         altitude = 245 << 4;
  196.         break;
  197.         
  198.         case 3:                    // This is striahgt across the lava pit.
  199.         altitude = 384 << 4;
  200.         break;
  201.     }
  202.     
  203.     return (altitude);
  204. }
  205.  
  206. //--------------------------------------------------------------  InitEnemy
  207.  
  208. // This resets an enemies info.  It is called when a new enemy is to be born.
  209. // It is called if an egg is about to hatch, if a new level has begun, or if…
  210. // if it is simply time to add a new enemy.
  211.  
  212. void InitEnemy (short i, Boolean reincarnated)
  213. {
  214.     Boolean        facing;
  215.     
  216.     if (spawnedEnemies < numEnemiesThisLevel)    // New enemy to appear (in other words…
  217.     {                                            // this enemy is not hatched).
  218.                                                 // Call function to set new location.
  219.         facing = SetEnemyInitialLocation(&theEnemies[i].dest);
  220.         theEnemies[i].wasDest = theEnemies[i].dest;
  221.         theEnemies[i].h = theEnemies[i].dest.left << 4;
  222.         theEnemies[i].v = theEnemies[i].dest.top << 4;
  223.         theEnemies[i].wasH = theEnemies[i].h;    // Reset "old locations" variables.
  224.         theEnemies[i].wasV = theEnemies[i].v;
  225.                                                 // Assign the "patrol altitude".
  226.         theEnemies[i].targetAlt = theEnemies[i].v - (40 << 4);
  227.         theEnemies[i].hVel = 0;                    // Zero velocity vraiables.
  228.         theEnemies[i].vVel = 0;
  229.         theEnemies[i].pass = 0;                    // Zero number of times wrapped around.
  230.         if ((levelOn % 5) == 4)                    // If this is an Egg Wave…
  231.             theEnemies[i].mode = kEggTimer;        // set enemy in "wait to hatch" mode.
  232.         else                                    // Otherwise, just sut enemy in…
  233.             theEnemies[i].mode = kIdle;            // idle mode.
  234.         if (i < numOwls)                        // Determine what kind of enemy.
  235.             theEnemies[i].kind = kOwl;
  236.         else if (i > (numOwls + 6))    
  237.             theEnemies[i].kind = kJackal;
  238.         else
  239.             theEnemies[i].kind = kWolf;
  240.         theEnemies[i].facingRight = facing;
  241.         SetEnemyAttributes(i);                    // Initialize enemy attributes.
  242.         
  243.         if (reincarnated)                        // If this is an egg that will hatch…
  244.             theEnemies[i].frame = RandomInt(48) + 8 + (numOwls * 32);
  245.         else
  246.             theEnemies[i].frame = RandomInt(48) + 32 + (64 * i) + (numOwls * 32);
  247.         
  248.         if ((levelOn % 5) == 4)                    // If this is an Egg Wave
  249.             theEnemies[i].kind--;                // Decrement "kind" (since it's incremented…
  250.                                                 // when they hatch).
  251.         spawnedEnemies++;                        // Keep track of number of enemies active.
  252.     }
  253. }
  254.  
  255. //--------------------------------------------------------------  GenerateEnemies
  256.  
  257. // This function is called only for a new level.  It goes through and…
  258. // intializes a whole host of enemies in one go.
  259.  
  260. void GenerateEnemies (void)
  261. {
  262.     short        i;
  263.     
  264.     if ((levelOn % 5) == 4)            // If this is an Egg Wave…
  265.     {
  266.         numEnemies = kMaxEnemies;    // we insist upon the maximum number of enemies.
  267.         numEnemiesThisLevel = numEnemies;
  268.     }
  269.     else                            // If not an egg wave, use a formula to determine…
  270.     {                                // the max number of enemies that are to be active.
  271.         numEnemies = ((levelOn / 5) + 2) * 2;
  272.         if (numEnemies > kMaxEnemies)
  273.             numEnemies = kMaxEnemies;
  274.         numEnemiesThisLevel = numEnemies * 2;
  275.     }
  276.     
  277.     deadEnemies = 0;                // No dead enemies yet.
  278.     
  279.                                     // Use formula to determine the number of owls…
  280.                                     // to appear.  This number goes down as the levels…
  281.                                     // increase.  It is used not merely to determine…
  282.                                     // how many owls are to appear, but also how many…
  283.                                     // of the more advanced enemies.  For example, when…
  284.                                     // numOwls goes down to zero, all the enemies will…
  285.                                     // be of the more advanced breed (wolves and jackals).
  286.     numOwls = 4 - ((levelOn + 2) / 5);
  287.     if (numOwls < 0)
  288.         numOwls = 0;
  289.     
  290.     spawnedEnemies = 0;                // No enemies have been "born" yet.
  291.                                     // Go through and set up all the enemies.
  292.     for (i = 0; i < numEnemies; i++)
  293.         InitEnemy(i, FALSE);
  294. }
  295.  
  296. //--------------------------------------------------------------  CheckEnemyPlatformHit
  297.  
  298. // This is the enemy counterpart to a similarly named function that tests for…
  299. // player collsions with the platforms.
  300.  
  301. void CheckEnemyPlatformHit (short h)
  302. {
  303.     Rect        hRect, vRect, whoCares;
  304.     short        i, offset;
  305.     
  306.     for (i = 0; i < numLedges; i++)                    // Test all platforms.
  307.     {                                                // Do a simple bounds test.
  308.         if (SectRect(&theEnemies[h].dest, &platformRects[i], &whoCares))
  309.         {                                            // If the enemy has hit the platform…
  310.             hRect.left = theEnemies[h].dest.left;    // Determine if enemy hit platform sides.
  311.             hRect.right = theEnemies[h].dest.right;
  312.             hRect.top = theEnemies[h].wasDest.top;
  313.             hRect.bottom = theEnemies[h].wasDest.bottom;
  314.                                                     // Test this new special rect to see if…
  315.                                                     // the enemy hit on of the platform sides.
  316.             if (SectRect(&hRect, &platformRects[i], &whoCares))
  317.             {                                        // If enemy hit from side, see which side.
  318.                                                     // We handle left and right seperatrely…
  319.                                                     // so that there's no ambiguity as to…
  320.                                                     // what the new velocity and location…
  321.                                                     // of the enemy is.  If we did not do it…
  322.                                                     // this way, there is the chance that an…
  323.                                                     // enemy get's "stuck" on the edge of…
  324.                                                     // a platform (due to round-off errors).
  325.                 if (theEnemies[h].h > theEnemies[h].wasH)
  326.                 {                                    // Enemy was moving right (hit left side).
  327.                     offset = theEnemies[h].dest.right - platformRects[i].left;
  328.                                                     // Slide enemy "off" platform.
  329.                     theEnemies[h].dest.left -= offset;
  330.                     theEnemies[h].dest.right -= offset;
  331.                     theEnemies[h].h = theEnemies[h].dest.left << 4;
  332.                     theEnemies[h].wasH = theEnemies[h].h;
  333.                                                     // Bounce enemy (negate velocity).
  334.                     if (theEnemies[h].hVel > 0)
  335.                         theEnemies[h].hVel = -(theEnemies[h].hVel >> 1);
  336.                     else
  337.                         theEnemies[h].hVel = theEnemies[h].hVel >> 1;
  338.                 }
  339.                 if (theEnemies[h].h < theEnemies[h].wasH)
  340.                 {                                    // Enemy was moving left (hit right side).
  341.                     offset = platformRects[i].right - theEnemies[h].dest.left;
  342.                                                     // Slide enemy "off" platform.
  343.                     theEnemies[h].dest.left += offset;
  344.                     theEnemies[h].dest.right += offset;
  345.                     theEnemies[h].h = theEnemies[h].dest.left << 4;
  346.                     theEnemies[h].wasH = theEnemies[h].h;
  347.                                                     // Bounce enemy (negate velocity).
  348.                     if (theEnemies[h].hVel < 0)
  349.                         theEnemies[h].hVel = -(theEnemies[h].hVel >> 1);
  350.                     else
  351.                         theEnemies[h].hVel = theEnemies[h].hVel >> 1;
  352.                 }
  353.                 doEnemyScrapeSound = TRUE;            // Play a collision sound.
  354.                                                     // Flip enemy to face opposite direction.
  355.                 theEnemies[h].facingRight = !theEnemies[h].facingRight;
  356.             }
  357.             else                                    // Enemy didn't hit from side.
  358.             {                                        // See if enemy hit top/bottom.
  359.                 vRect.left = theEnemies[h].wasDest.left;
  360.                 vRect.right = theEnemies[h].wasDest.right;
  361.                 vRect.top = theEnemies[h].dest.top;
  362.                 vRect.bottom = theEnemies[h].dest.bottom;
  363.                                                     // Special "test rect" for top/bottom hit.
  364.                 if (SectRect(&vRect, &platformRects[i], &whoCares))
  365.                 {                                    // If hit the top/bottom of platform…
  366.                     if (theEnemies[h].mode == kFalling)
  367.                     {                                // Was the enemy a falling egg?
  368.                                                     // Bounce egg (with some inelasticity).
  369.                         theEnemies[i].hVel -= (theEnemies[i].hVel >> 3);
  370.                                                     // When the eggs velocity is between…
  371.                                                     // +/- 8, consider the egg at rest.
  372.                         if ((theEnemies[i].hVel < 8) && (theEnemies[i].hVel > -8))
  373.                         {
  374.                             if (theEnemies[i].hVel > 0)
  375.                                 theEnemies[i].hVel--;
  376.                             else if (theEnemies[i].hVel < 0)
  377.                                 theEnemies[i].hVel++;
  378.                         }
  379.                     }
  380.                                                     // Specifically, did enemy hit the top?
  381.                     if (theEnemies[h].v > theEnemies[h].wasV)
  382.                     {                                // Enemy heading down (hit platform top).
  383.                         offset = theEnemies[h].dest.bottom - platformRects[i].top;
  384.                                                     // Move enemy up off platform.
  385.                         theEnemies[h].dest.top -= offset;
  386.                         theEnemies[h].dest.bottom -= offset;
  387.                         theEnemies[h].v = theEnemies[h].dest.top << 4;
  388.                         theEnemies[h].wasV = theEnemies[h].v;
  389.                         if (theEnemies[h].vVel > kDontFlapVel)
  390.                             doEnemyScrapeSound = TRUE;
  391.                                                     // "Bounce" enemy.
  392.                         if (theEnemies[h].vVel > 0)
  393.                             theEnemies[h].vVel = -(theEnemies[h].vVel >> 1);
  394.                         else
  395.                             theEnemies[h].vVel = theEnemies[h].vVel >> 1;
  396.                         if ((theEnemies[h].vVel < 8) && (theEnemies[h].vVel > -8) && 
  397.                                 (theEnemies[h].hVel == 0) && (theEnemies[h].mode == kFalling))
  398.                         {                        // Here we handle an egg come to rest.
  399.                             if (((theEnemies[h].dest.right - 8) > platformRects[i].right) && 
  400.                                     (theEnemies[h].hVel == 0))
  401.                             {                    // Special case where egg right on edge.
  402.                                 theEnemies[h].hVel = 32;
  403.                             }
  404.                             else if (((theEnemies[h].dest.left + 8) < platformRects[i].left) && 
  405.                                     (theEnemies[h].hVel == 0))
  406.                             {                    // Special case where egg right on edge.
  407.                                 theEnemies[h].hVel = -32;
  408.                             }
  409.                             else                // If egg not on the edge of platform…
  410.                             {                    // switch to "timer" mode.
  411.                                 theEnemies[h].mode = kEggTimer;
  412.                                 theEnemies[h].frame = (numOwls * 96) + 128;
  413.                                 theEnemies[h].vVel = 0;
  414.                             }
  415.                         }
  416.                     }
  417.                     if (theEnemies[h].v < theEnemies[h].wasV)
  418.                     {                            // Enemy was rising - hit bottom of platform.
  419.                         offset = theEnemies[h].dest.top - platformRects[i].bottom;
  420.                                                 // Slide enemy off platform.
  421.                         theEnemies[h].dest.top -= offset;
  422.                         theEnemies[h].dest.bottom -= offset;
  423.                         theEnemies[h].v = theEnemies[h].dest.top << 4;
  424.                         theEnemies[h].wasV = theEnemies[h].v;
  425.                                                 // Play collision sound.
  426.                         doEnemyScrapeSound = TRUE;
  427.                                                 // "Bounce" enemy downward from platform.
  428.                         if (theEnemies[h].vVel < 0)
  429.                             theEnemies[h].vVel = -(theEnemies[h].vVel >> 2);
  430.                         else
  431.                             theEnemies[h].vVel = theEnemies[h].vVel >> 2;
  432.                         if ((theEnemies[h].vVel < 8) && (theEnemies[h].vVel > -8) && 
  433.                                 (theEnemies[h].hVel == 0) && (theEnemies[h].mode == kFalling))
  434.                         {
  435.                             theEnemies[h].mode = kEggTimer;
  436.                             theEnemies[h].frame = (numOwls * 96) + 128;
  437.                             theEnemies[h].vVel = 0;
  438.                         }
  439.                     }
  440.                 }
  441.             }
  442.         }
  443.     }
  444. }
  445.  
  446. //--------------------------------------------------------------  CheckEnemyRoofCollision
  447.  
  448. // Like the player counterpart, this function checks to see if an enemy has hit…
  449. // the ceiling or the lava.  It handles the consequences of both cases.
  450.  
  451. void CheckEnemyRoofCollision (short i)
  452. {
  453.     short        offset;
  454.     
  455.     if (theEnemies[i].dest.top < (kRoofHeight - 2))
  456.     {                    // If enemy has hit the ceiling…
  457.         offset = kRoofHeight - theEnemies[i].dest.top;
  458.                         // Move enemy down to a "legal" altitude.
  459.         theEnemies[i].dest.top += offset;
  460.         theEnemies[i].dest.bottom += offset;
  461.         theEnemies[i].v = theEnemies[i].dest.top << 4;
  462.                         // Play a collision sound.
  463.         doEnemyScrapeSound = TRUE;
  464.                         // Bounce enemy downward.
  465.         theEnemies[i].vVel = -(theEnemies[i].vVel >> 2);
  466.     }
  467.     else if (theEnemies[i].dest.top > kLavaHeight)
  468.     {                    // If enemy has fallen into lava…
  469.                         // kill that enemy.
  470.         theEnemies[i].mode = kDeadAndGone;
  471.         deadEnemies++;
  472.                         // Play a splash sound.
  473.         PlayExternalSound(kSplashSound, kSplashPriority);
  474.                         // Call up another from the ranks.
  475.         InitEnemy(i, TRUE);
  476.     }
  477. }
  478.  
  479. //--------------------------------------------------------------  HandleIdleEnemies
  480.  
  481. // The following functions handle the various enemy modes.  Enemies are…
  482. // considered to be in a specific mode and each mode is handled differently.
  483. // Idle enemies are ones who are "invisible" - not yet born.  While idle, a…
  484. // timer is ticking down - when it reaches zero, the enemy appears.
  485.  
  486. void HandleIdleEnemies (short i)
  487. {
  488.     theEnemies[i].frame--;                    // Decrement timer.
  489.     if (theEnemies[i].frame <= 0)            // If timer is zero or less…
  490.     {
  491.         theEnemies[i].mode = kSpawning;        // enemy is "born".
  492.         theEnemies[i].wasH = theEnemies[i].h;
  493.         theEnemies[i].wasV = theEnemies[i].v;
  494.         theEnemies[i].hVel = 0;
  495.         theEnemies[i].vVel = 0;
  496.         theEnemies[i].frame = 0;
  497.         SetEnemyAttributes(i);                // Initialize enemy attributes.
  498.         PlayExternalSound(kSpawnSound, kSpawnPriority);
  499.     }
  500. }
  501.  
  502. //--------------------------------------------------------------  HandleFlyingEnemies
  503.  
  504. // Once an enemy takes off from a platform, they will always be in flying mode…
  505. // unless they should be killed.  This function handles the flying mode.
  506.  
  507. void HandleFlyingEnemies (short i)
  508. {
  509.     short        dist;
  510.     Boolean        shouldFlap;
  511.                                 // Take into account gravity pulling enemy down.
  512.     theEnemies[i].vVel += kGravity;
  513.                                 // Get absolute difference in enemy/player altitude.
  514.     dist = thePlayer.dest.top - theEnemies[i].dest.top;
  515.     if (dist < 0)
  516.         dist = -dist;
  517.                                 // See if the player is within the enemy's "seek" range.
  518.     if ((dist < theEnemies[i].heightSmell) && 
  519.             ((thePlayer.mode == kFlying) || (thePlayer.mode == kWalking)))
  520.     {                            // Enemy will actively seek the player.
  521.         if (thePlayer.dest.left < theEnemies[i].dest.left)
  522.         {                        // Determine if quicker to go left or right to get player.
  523.             dist = theEnemies[i].dest.left - thePlayer.dest.left;
  524.             if (dist < 320)        // Closest route is to the left.
  525.                 theEnemies[i].facingRight = FALSE;
  526.             else                // Closest route is to the right.
  527.                 theEnemies[i].facingRight = TRUE;
  528.         }
  529.         else if (thePlayer.dest.left > theEnemies[i].dest.left)
  530.         {                        // Determine if quicker to go left or right to get player.
  531.             dist = thePlayer.dest.left - theEnemies[i].dest.left;
  532.             if (dist < 320)        // Closest route is to the right.
  533.                 theEnemies[i].facingRight = TRUE;
  534.             else                // Closest route is to the left.
  535.                 theEnemies[i].facingRight = FALSE;
  536.         }
  537.                                 // Seek an altitude 16 pixels above player.
  538.         if (((theEnemies[i].v + 16) > thePlayer.v) && (evenFrame))
  539.             shouldFlap = TRUE;
  540.         else
  541.             shouldFlap = FALSE;
  542.     }
  543.     else                        // Else, player not within enemy's "seek" altitude.
  544.     {                            // Flap if necessary to maintain "patrol altitude".
  545.         if ((theEnemies[i].v > theEnemies[i].targetAlt) && (evenFrame))
  546.             shouldFlap = TRUE;
  547.         else
  548.             shouldFlap = FALSE;
  549.     }
  550.     
  551.     if (shouldFlap)                // If the enemy has determined that it needs to flap…
  552.     {                            // Give the enemy lift & play the flap sound.
  553.         theEnemies[i].vVel -= theEnemies[i].flapImpulse;
  554.         doEnemyFlapSound = TRUE;
  555.     }
  556.                                 // Enemy never hovers - must move right or left.
  557.     if (theEnemies[i].facingRight)
  558.     {                            // If enemy facing right - move enemy to the right.
  559.         theEnemies[i].hVel += kEnemyImpulse;
  560.         if (theEnemies[i].hVel > theEnemies[i].maxHVel)
  561.             theEnemies[i].hVel = theEnemies[i].maxHVel;
  562.                                 // Determine correct graphic for enemy.
  563.         switch (theEnemies[i].kind)
  564.         {
  565.             case kOwl:
  566.             if (shouldFlap)
  567.                 theEnemies[i].srcNum = 12;
  568.             else
  569.                 theEnemies[i].srcNum = 13;
  570.             break;
  571.             
  572.             case kWolf:
  573.             if (shouldFlap)
  574.                 theEnemies[i].srcNum = 16;
  575.             else
  576.                 theEnemies[i].srcNum = 17;
  577.             break;
  578.             
  579.             case kJackal:
  580.             if (shouldFlap)
  581.                 theEnemies[i].srcNum = 20;
  582.             else
  583.                 theEnemies[i].srcNum = 21;
  584.             break;
  585.         }
  586.         
  587.     }
  588.     else                        // If enemy not facing right (left) move to the left.
  589.     {
  590.         theEnemies[i].hVel -= kEnemyImpulse;
  591.         if (theEnemies[i].hVel < -theEnemies[i].maxHVel)
  592.             theEnemies[i].hVel = -theEnemies[i].maxHVel;
  593.                                 // Determine correct graphic for enemy.
  594.         switch (theEnemies[i].kind)
  595.         {
  596.             case kOwl:
  597.             if (shouldFlap)
  598.                 theEnemies[i].srcNum = 14;
  599.             else
  600.                 theEnemies[i].srcNum = 15;
  601.             break;
  602.             
  603.             case kWolf:
  604.             if (shouldFlap)
  605.                 theEnemies[i].srcNum = 18;
  606.             else
  607.                 theEnemies[i].srcNum = 19;
  608.             break;
  609.             
  610.             case kJackal:
  611.             if (shouldFlap)
  612.                 theEnemies[i].srcNum = 22;
  613.             else
  614.                 theEnemies[i].srcNum = 23;
  615.             break;
  616.         }
  617.     }
  618.                                         // Move enemy horizontally based on hori velocity.
  619.     theEnemies[i].h += theEnemies[i].hVel;
  620.     theEnemies[i].dest.left = theEnemies[i].h >> 4;
  621.     theEnemies[i].dest.right = theEnemies[i].dest.left + 64;
  622.                                         // Move enemy vertically based on vertical velocity.
  623.     theEnemies[i].v += theEnemies[i].vVel;
  624.     theEnemies[i].dest.top = theEnemies[i].v >> 4;
  625.     theEnemies[i].dest.bottom = theEnemies[i].dest.top + 40;
  626.                                         // Check for wrap-around.
  627.     if (theEnemies[i].dest.left > 640)
  628.     {                                    // If off right edge, wrap around to left side.
  629.         OffsetRect(&theEnemies[i].dest, -640, 0);
  630.         theEnemies[i].h = theEnemies[i].dest.left << 4;
  631.         OffsetRect(&theEnemies[i].wasDest, -640, 0);
  632.         theEnemies[i].pass++;            // Increment number of "wrap-arounds" for this enemy.
  633.         if (theEnemies[i].pass > 2)        // After two screen passes (wrap arounds)…
  634.         {                                // enemy patrols a new altitude.
  635.             theEnemies[i].targetAlt = AssignNewAltitude();
  636.             theEnemies[i].pass = 0;
  637.         }
  638.     }
  639.     else if (theEnemies[i].dest.right < 0)
  640.     {                                    // If off left edge, wrap around to right side.
  641.         OffsetRect(&theEnemies[i].dest, 640, 0);
  642.         theEnemies[i].h = theEnemies[i].dest.left << 4;
  643.         OffsetRect(&theEnemies[i].wasDest, 640, 0);
  644.         theEnemies[i].pass++;
  645.         if (theEnemies[i].pass > 2)
  646.         {
  647.             theEnemies[i].targetAlt = AssignNewAltitude();
  648.             theEnemies[i].pass = 0;
  649.         }
  650.     }
  651.                                         // Throw a touch of friction into the mix.
  652.     theEnemies[i].vVel -= theEnemies[i].vVel >> 4;
  653.                                         // Keep enemies from moving excessively fast.
  654.     if (theEnemies[i].vVel > theEnemies[i].maxVVel)
  655.         theEnemies[i].vVel = theEnemies[i].maxVVel;
  656.     else if (theEnemies[i].vVel < -theEnemies[i].maxVVel)
  657.         theEnemies[i].vVel = -theEnemies[i].maxVVel;
  658.     
  659.     CheckEnemyRoofCollision(i);            // Check for lava/celing collisions.
  660.     CheckEnemyPlatformHit(i);            // Check for platform collisions.
  661. }
  662.  
  663. //--------------------------------------------------------------  HandleWalkingEnemy
  664.  
  665. // This is a brief mode for an enemy.  When an enemy has hatched from an egg, it…
  666. // walks only for 8 game frames at which point it takes off and flies for the rest…
  667. // of its life.
  668.  
  669. void HandleWalkingEnemy (short i)
  670. {
  671.     if (theEnemies[i].facingRight)        // If enemy facing right, walk to the right.
  672.     {
  673.         theEnemies[i].dest.left += 6;    // Move enemy to right.
  674.         theEnemies[i].dest.right += 6;
  675.         switch (theEnemies[i].kind)        // Determine correct graphic for walking enemy.
  676.         {
  677.             case kOwl:
  678.             theEnemies[i].srcNum = 1 - theEnemies[i].srcNum;
  679.             break;
  680.             
  681.             case kWolf:
  682.             theEnemies[i].srcNum = 9 - theEnemies[i].srcNum;
  683.             break;
  684.             
  685.             case kJackal:
  686.             theEnemies[i].srcNum = 17 - theEnemies[i].srcNum;
  687.             break;
  688.         }
  689.         theEnemies[i].hVel = 6 << 4;
  690.     }
  691.     else                                // If enemy not facing right (left), walk to the left.
  692.     {
  693.         theEnemies[i].dest.left -= 6;    // Move enemy to left.
  694.         theEnemies[i].dest.right -= 6;
  695.         switch (theEnemies[i].kind)        // Determine correct graphic for walking enemy.
  696.         {
  697.             case kOwl:
  698.             theEnemies[i].srcNum = 5 - theEnemies[i].srcNum;
  699.             break;
  700.             
  701.             case kWolf:
  702.             theEnemies[i].srcNum = 13 - theEnemies[i].srcNum;
  703.             break;
  704.             
  705.             case kJackal:
  706.             theEnemies[i].srcNum = 21 - theEnemies[i].srcNum;
  707.             break;
  708.         }
  709.         theEnemies[i].hVel = -6 << 4;
  710.     }
  711.     
  712.     theEnemies[i].frame++;                // Increment number of frames it has walked for.
  713.     if (theEnemies[i].frame >= 8)        // If over 8, enemy takes off an flies.
  714.     {
  715.         theEnemies[i].mode = kFlying;    // Switch to flying mode.
  716.         theEnemies[i].frame = 0;        // Reset "frame" variable.
  717.         switch (theEnemies[i].kind)        // Determine correct graphic for flying enemy.
  718.         {
  719.             case kOwl:
  720.             if (theEnemies[i].facingRight)
  721.                 theEnemies[i].srcNum = 12;
  722.             else
  723.                 theEnemies[i].srcNum = 14;
  724.             break;
  725.             
  726.             case kWolf:
  727.             if (theEnemies[i].facingRight)
  728.                 theEnemies[i].srcNum = 16;
  729.             else
  730.                 theEnemies[i].srcNum = 18;
  731.             break;
  732.             
  733.             case kJackal:
  734.             if (theEnemies[i].facingRight)
  735.                 theEnemies[i].srcNum = 20;
  736.             else
  737.                 theEnemies[i].srcNum = 22;
  738.             break;
  739.         }
  740.                                         // Re-size enemy bounds to a "flying" size.
  741.         theEnemies[i].dest.left -= 8;
  742.         theEnemies[i].dest.right += 8;
  743.         theEnemies[i].dest.bottom = theEnemies[i].dest.top + 40;
  744.         theEnemies[i].h = theEnemies[i].dest.left * 16;
  745.         theEnemies[i].v = theEnemies[i].dest.top * 16;
  746.     }
  747. }
  748.  
  749. //--------------------------------------------------------------  HandleSpawningEnemy
  750.  
  751. // This is an enemy "rising out of a platform".  Either an egg has just hatched…
  752. // or a brand new enemy has been introduced.  Irregardless, the sphinx is born.
  753. // When the enemy is at its full height, it will begin to walk.
  754.  
  755. void HandleSpawningEnemy (short i)
  756. {
  757.     theEnemies[i].frame++;                // Advance timer.
  758.     if (theEnemies[i].frame >= 48)        // If timer >= 48, enemy begins to walk.
  759.     {
  760.         theEnemies[i].mode = kWalking;
  761.         theEnemies[i].frame = 0;
  762.         
  763.         switch (theEnemies[i].kind)        // Determine appropriate graphic.
  764.         {
  765.             case kOwl:
  766.             if (theEnemies[i].facingRight)
  767.                 theEnemies[i].srcNum = 0;
  768.             else
  769.                 theEnemies[i].srcNum = 2;
  770.             break;
  771.             
  772.             case kWolf:
  773.             if (theEnemies[i].facingRight)
  774.                 theEnemies[i].srcNum = 4;
  775.             else
  776.                 theEnemies[i].srcNum = 6;
  777.             break;
  778.             
  779.             case kJackal:
  780.             if (theEnemies[i].facingRight)
  781.                 theEnemies[i].srcNum = 8;
  782.             else
  783.                 theEnemies[i].srcNum = 10;
  784.             break;
  785.         }
  786.     }
  787.     else                            // If not full height, use "timer" to determine height.
  788.         theEnemies[i].dest.top = theEnemies[i].dest.bottom - theEnemies[i].frame;
  789. }
  790.  
  791. //--------------------------------------------------------------  HandleFallingEnemy
  792.  
  793. // A "falling" enemy is an air borne egg.  The enemy was killed, turned into an egg, …
  794. // and the egg is in freefall.  If the egg comes to rest, it will begin a countdown…
  795. // until it is hatched.
  796.  
  797. void HandleFallingEnemy (short i)
  798. {                                    // Take into account gravity - accelerate egg down.
  799.     theEnemies[i].vVel += kGravity;
  800.                                     // Don't allow velocities to skyrocket.
  801.     if (theEnemies[i].vVel > theEnemies[i].maxVVel)
  802.         theEnemies[i].vVel = theEnemies[i].maxVVel;
  803.     else if (theEnemies[i].vVel < -theEnemies[i].maxVVel)
  804.         theEnemies[i].vVel = -theEnemies[i].maxVVel;
  805.     
  806.     if (evenFrame)                    // Apply friction on even frames (who knows).
  807.     {                                // "Friction" is 1/32nd of the velocity.
  808.         theEnemies[i].hVel -= (theEnemies[i].hVel >> 5);
  809.         if ((theEnemies[i].hVel < 32) && (theEnemies[i].hVel > -32))
  810.         {
  811.             if (theEnemies[i].hVel > 0)
  812.                 theEnemies[i].hVel--;
  813.             else if (theEnemies[i].hVel < 0)
  814.                 theEnemies[i].hVel++;
  815.         }
  816.     }
  817.                                     // Move egg horizontally.
  818.     theEnemies[i].h += theEnemies[i].hVel;
  819.     theEnemies[i].dest.left = theEnemies[i].h >> 4;
  820.     theEnemies[i].dest.right = theEnemies[i].dest.left + 24;
  821.                                     // Move egg vertically.
  822.     theEnemies[i].v += theEnemies[i].vVel;
  823.     theEnemies[i].dest.top = theEnemies[i].v >> 4;
  824.     theEnemies[i].dest.bottom = theEnemies[i].dest.top + 24;
  825.                                     // Check for wrap around.
  826.     if (theEnemies[i].dest.left > 640)
  827.     {
  828.         OffsetRect(&theEnemies[i].dest, -640, 0);
  829.         theEnemies[i].h = theEnemies[i].dest.left << 4;
  830.         OffsetRect(&theEnemies[i].wasDest, -640, 0);
  831.     }
  832.     else if (theEnemies[i].dest.right < 0)
  833.     {
  834.         OffsetRect(&theEnemies[i].dest, 640, 0);
  835.         theEnemies[i].h = theEnemies[i].dest.left << 4;
  836.         OffsetRect(&theEnemies[i].wasDest, 640, 0);
  837.     }
  838.     
  839.     CheckEnemyRoofCollision(i);    // See if egg hit ceiling or lava.
  840.     CheckEnemyPlatformHit(i);    // Handle platform hit (it is here it determines if…
  841.                                 // egg has come to rest and should begin countdown).
  842. }
  843.  
  844. //--------------------------------------------------------------  HandleEggEnemy
  845.  
  846. // This is the "idle" egg mode.  This is a static egg, sitting peacefully on…
  847. // a platform.  Waiting patiently so it might hatch into a death-sphinx and…
  848. // slaughter the player.
  849.  
  850. void HandleEggEnemy (short i)
  851. {
  852.     short        center;
  853.     
  854.     theEnemies[i].frame--;                // Decrement the egg timer!
  855.     if (theEnemies[i].frame < 24)        // When it falls below 24, egg starts shrinking.
  856.     {                                    // Use "frame" to determine height of egg.
  857.         theEnemies[i].dest.top = theEnemies[i].dest.bottom - theEnemies[i].frame;
  858.         if (theEnemies[i].frame <= 0)    // When the egg is completely flat (gone)…
  859.         {                                // then BOOM! a sphinx is spawned!
  860.             theEnemies[i].frame = 0;
  861.             PlayExternalSound(kSpawnSound, kSpawnPriority);
  862.             center = (theEnemies[i].dest.left + theEnemies[i].dest.right) >> 1;
  863.                                         // Resize enemy bounds to new "walking enemy" size.
  864.             theEnemies[i].dest.left = center - 24;
  865.             theEnemies[i].dest.right = center + 24;
  866.             theEnemies[i].wasDest = theEnemies[i].dest;
  867.             theEnemies[i].h = theEnemies[i].dest.left << 4;
  868.             theEnemies[i].v = theEnemies[i].dest.top << 4;
  869.                                         // Set up all other enemy variables.
  870.             theEnemies[i].wasH = theEnemies[i].h;
  871.             theEnemies[i].wasV = theEnemies[i].v;
  872.             theEnemies[i].hVel = 0;
  873.             theEnemies[i].vVel = 0;
  874.             theEnemies[i].mode = kSpawning;
  875.             theEnemies[i].kind++;
  876.             if (theEnemies[i].kind > kJackal)
  877.                 theEnemies[i].kind = kJackal;
  878.             SetEnemyAttributes(i);
  879.         }
  880.     }
  881. }
  882.  
  883. //--------------------------------------------------------------  MoveEnemies
  884.  
  885. // This is the "master" enemy function.  It goes through all the enemies…
  886. // and calls the above functions depending upon an enemy's mode.
  887.  
  888. void MoveEnemies (void)
  889. {
  890.     short        i;
  891.     
  892.     doEnemyFlapSound = FALSE;        // Intially, assume no flap or scrape sounds.
  893.     doEnemyScrapeSound = FALSE;
  894.                                     // Go through each enemy.
  895.     for (i = 0; i < numEnemies; i++)
  896.     {
  897.         switch (theEnemies[i].mode)
  898.         {                            // Handle enemy according to mode it is in.
  899.             case kIdle:                // Enemy not born yet.
  900.             HandleIdleEnemies(i);
  901.             break;
  902.             
  903.             case kFlying:            // Enemy air borne.
  904.             HandleFlyingEnemies(i);
  905.             break;
  906.             
  907.             case kWalking:            // Enemy just born, walking off platform.
  908.             HandleWalkingEnemy(i);
  909.             break;
  910.             
  911.             case kSpawning:            // Enemy growing from a platform.
  912.             HandleSpawningEnemy(i);
  913.             break;
  914.             
  915.             case kFalling:            // Enemy is an egg in flight.
  916.             HandleFallingEnemy(i);
  917.             break;
  918.             
  919.             case kEggTimer:            // Enemy is a patient, idle, silent egg.
  920.             HandleEggEnemy(i);
  921.             break;
  922.             
  923.             case kDeadAndGone:        // Enemy no more - gone for good this level.
  924.             break;
  925.         }
  926.     }
  927.                                     // If any sounds were flagged, play them.
  928.     if (doEnemyFlapSound)
  929.         PlayExternalSound(kFlap2Sound, kFlap2Priority);
  930.     if (doEnemyScrapeSound)
  931.         PlayExternalSound(kScrape2Sound, kScrape2Priority);
  932.                                     // See if enough enemies were killed to advance to…
  933.                                     // next level (wave).
  934.     if ((deadEnemies >= numEnemiesThisLevel) && (countDownTimer == 0))
  935.         countDownTimer = 30;
  936. }
  937.  
  938. //--------------------------------------------------------------  InitHandLocation
  939.  
  940. // This simply sets up the hand.  Puts it deep in the lava (off bottom of screen).
  941.  
  942. void InitHandLocation (void)
  943. {
  944.     SetRect(&theHand.dest, 0, 0, 56, 57);
  945.     OffsetRect(&theHand.dest, 48, 460);
  946. }
  947.  
  948. //--------------------------------------------------------------  HandleHand
  949.  
  950. // This is the hand "AI".  The hand, like the sphinx enemies, has modes.
  951.  
  952. void HandleHand (void)
  953. {
  954.     Rect        whoCares;
  955.     short        hDiff, vDiff, pull, speed;
  956.     
  957.     switch (theHand.mode)
  958.     {
  959.         case kLurking:                // Hand is down, waiting for player to stray near.
  960.         if ((thePlayer.mode == kFlying) && (SectRect(&thePlayer.dest, &grabZone, &whoCares)))
  961.         {                            // If player flies near, hand begins to reach out.
  962.             theHand.mode = kOutGrabeth;
  963.             InitHandLocation();
  964.         }
  965.         break;
  966.         
  967.         case kOutGrabeth:            // Hand is either coming after or has a hold of player.
  968.         case kClutching:
  969.         if (SectRect(&thePlayer.dest, &grabZone, &whoCares))
  970.         {                            // See if player in the "grab/clutch zone".
  971.             hDiff = theHand.dest.left - thePlayer.dest.left;
  972.             vDiff = theHand.dest.top - thePlayer.dest.top;
  973.                                     // Ah!  Player caught.  Move player to correct…
  974.                                     // location relative to the hand (so the player…
  975.                                     // appears to, in fact, be held).
  976.             if (thePlayer.facingRight)
  977.                 hDiff -= 3;
  978.             else
  979.                 hDiff -= 21;
  980.             vDiff -= 29;
  981.                                     // How hard/fast the hand moves depends on level.
  982.             speed = (levelOn >> 3) + 1;
  983.             if (hDiff < 0)
  984.             {
  985.                 theHand.dest.left += speed;
  986.                 theHand.dest.right += speed;
  987.             }
  988.             else if (hDiff > 0)
  989.             {
  990.                 theHand.dest.left -= speed;
  991.                 theHand.dest.right -= speed;
  992.             }
  993.             if (vDiff < 0)
  994.             {
  995.                 theHand.dest.top += speed;
  996.                 theHand.dest.bottom += speed;
  997.             }
  998.             else if (vDiff > 0)
  999.             {
  1000.                 theHand.dest.top -= speed;
  1001.                 theHand.dest.bottom -= speed;
  1002.             }
  1003.                                     // Determine absolute distance player is from hand.
  1004.             if (hDiff < 0)
  1005.                 hDiff = -hDiff;
  1006.             if (vDiff < 0)
  1007.                 vDiff = -vDiff;
  1008.             
  1009.             if ((hDiff < 8) && (vDiff < 8))
  1010.             {                        // If player in the "hot zone", player is nabbed!
  1011.                 theHand.mode = kClutching;
  1012.                 thePlayer.clutched = TRUE;
  1013.                                     // Player's movement is severely dampened.
  1014.                 thePlayer.hVel = thePlayer.hVel >> 3;
  1015.                 thePlayer.vVel = thePlayer.vVel >> 3;
  1016.                                     // Hand pulls player down (strength is greater on…
  1017.                                     // higher levels).
  1018.                 pull = levelOn << 2;
  1019.                 if (pull > 48)        // Set an absolute limit on hand strength.
  1020.                     pull = 48;
  1021.                                     // Pull player donw!
  1022.                 thePlayer.vVel += pull;
  1023.                 theHand.dest.top = thePlayer.dest.top + 29;
  1024.                 theHand.dest.bottom = theHand.dest.top + 57;
  1025.                 if (thePlayer.facingRight)
  1026.                     theHand.dest.left = thePlayer.dest.left + 3;
  1027.                 else
  1028.                     theHand.dest.left = thePlayer.dest.left + 21;
  1029.                 theHand.dest.right = theHand.dest.left + 58;
  1030.             }
  1031.             else                    // If player not in "sweet spot", hand is seeking.
  1032.             {
  1033.                 thePlayer.clutched = FALSE;
  1034.                 theHand.mode = kOutGrabeth;
  1035.             }
  1036.         }
  1037.         else                        // Player not even close to hand…
  1038.         {                            // Hand sinks back down into lava.
  1039.             theHand.dest.top++;
  1040.             theHand.dest.bottom++;
  1041.                                     // When hand is off screen, hand resumes lurking.
  1042.             if (theHand.dest.top > 460)
  1043.                 theHand.mode = kLurking;
  1044.             else
  1045.                 theHand.mode = kOutGrabeth;
  1046.             thePlayer.clutched = FALSE;
  1047.         }
  1048.         break;
  1049.     }
  1050. }
  1051.  
  1052. //--------------------------------------------------------------  InitEye
  1053.  
  1054. // This initializes all the eye's variables.
  1055.  
  1056. void InitEye (void)
  1057. {
  1058.     SetRect(&theEye.dest, 0, 0, 48, 31);
  1059.     OffsetRect(&theEye.dest, 296, 97);
  1060.     theEye.mode = kWaiting;
  1061.     theEye.frame = (numOwls + 2) * 720;
  1062.     theEye.srcNum = 0;
  1063.     theEye.opening = 1;
  1064.     theEye.killed = FALSE;
  1065.     theEye.entering = FALSE;
  1066. }
  1067.  
  1068. //--------------------------------------------------------------  KillOffEye
  1069.  
  1070. // This function handles a "slain" eye!
  1071.  
  1072. void KillOffEye (void)
  1073. {
  1074.     if (theEye.mode == kStalking)
  1075.     {
  1076.         theEye.killed = TRUE;
  1077.         theEye.opening = 1;
  1078.         theEye.entering = FALSE;
  1079.         if (theEye.srcNum == 0)
  1080.             theEye.srcNum = 1;
  1081.     }
  1082.     else
  1083.         InitEye();
  1084. }
  1085.  
  1086. //--------------------------------------------------------------  HandleEye
  1087.  
  1088. // But of course, the eye has modes as well.  This function handles the eye…
  1089. // depending upon the mode it is in.
  1090.  
  1091. void HandleEye (void)
  1092. {
  1093.     short        diffH, diffV, speed;
  1094.     
  1095.     if (theEye.mode == kStalking)        // Eye is alive!
  1096.     {
  1097.         speed = (levelOn >> 4) + 1;        // How fast it moves depends on level.
  1098.         if (speed > 3)
  1099.             speed = 3;
  1100.                                         // When eye appears or dies, it is stationary.
  1101.         if ((theEye.killed) || (theEye.entering))
  1102.         {
  1103.             speed = 0;
  1104.         }
  1105.         else if ((thePlayer.mode != kFlying) && (thePlayer.mode != kWalking))
  1106.         {
  1107.             diffH = theEye.dest.left - 296;
  1108.             diffV = theEye.dest.bottom - 128;
  1109.         }
  1110.         else
  1111.         {
  1112.             diffH = theEye.dest.left - thePlayer.dest.left;
  1113.             diffV = theEye.dest.bottom - thePlayer.dest.bottom;
  1114.         }
  1115.                                         // Find direction to player (no wrap-around for eye).
  1116.         if (diffH > 0)
  1117.         {
  1118.             if (diffH < speed)
  1119.                 theEye.dest.left -= diffH;
  1120.             else
  1121.                 theEye.dest.left -= speed;
  1122.             theEye.dest.right = theEye.dest.left + 48;
  1123.         }
  1124.         else if (diffH < 0)
  1125.         {
  1126.             if (-diffH < speed)
  1127.                 theEye.dest.left -= diffH;
  1128.             else
  1129.                 theEye.dest.left += speed;
  1130.             theEye.dest.right = theEye.dest.left + 48;
  1131.         }
  1132.         if (diffV > 0)
  1133.         {
  1134.             if (diffV < speed)
  1135.                 theEye.dest.bottom -= diffV;
  1136.             else
  1137.                 theEye.dest.bottom -= speed;
  1138.             theEye.dest.top = theEye.dest.bottom - 31;
  1139.         }
  1140.         else if (diffV < 0)
  1141.         {
  1142.             if (-diffV < speed)
  1143.                 theEye.dest.bottom -= diffV;
  1144.             else
  1145.                 theEye.dest.bottom += speed;
  1146.             theEye.dest.top = theEye.dest.bottom - 31;
  1147.         }
  1148.         
  1149.         theEye.frame++;                    // Increment eye frame (timer).
  1150.                                         // Determine correct graphic for eye.
  1151.         if (theEye.srcNum != 0)
  1152.         {
  1153.             if (theEye.frame > 3)        // "Eye-closing frame" holds for 3 frames.
  1154.             {
  1155.                 theEye.frame = 0;
  1156.                 theEye.srcNum += theEye.opening;
  1157.                 if (theEye.srcNum > 3)
  1158.                 {
  1159.                     theEye.srcNum = 3;
  1160.                     theEye.opening = -1;
  1161.                     if (theEye.killed)
  1162.                         InitEye();
  1163.                 }
  1164.                 else if (theEye.srcNum <= 0)
  1165.                 {
  1166.                     theEye.srcNum = 0;
  1167.                     theEye.opening = 1;
  1168.                     theEye.frame = 0;
  1169.                     theEye.entering = FALSE;
  1170.                 }
  1171.             }
  1172.         }
  1173.         else if (theEye.frame > 256)
  1174.         {
  1175.             theEye.srcNum = 1;
  1176.             theEye.opening = 1;
  1177.             theEye.frame = 0;
  1178.         }
  1179.                                         // Get absolute distance from eye to player.
  1180.         diffH = theEye.dest.left - thePlayer.dest.left;
  1181.         diffV = theEye.dest.bottom - thePlayer.dest.bottom;
  1182.         if (diffH < 0)
  1183.             diffH = -diffH;
  1184.         if (diffV < 0)
  1185.             diffV = -diffV;
  1186.                                         // See if player close enough to be killed!
  1187.         if ((diffH < 16) && (diffV < 16) && (!theEye.entering) && 
  1188.                 (!theEye.killed))        // Close enough to call it a kill.
  1189.         {
  1190.             if (theEye.srcNum == 0)        // If eye was open, player is killed.
  1191.             {                            // Strike lightning (hit the player).
  1192.                 if (lightningCount == 0)
  1193.                 {
  1194.                     lightH = thePlayer.dest.left + 24;
  1195.                     lightV = thePlayer.dest.bottom - 24;
  1196.                     lightningCount = 6;    // Strike 6 times!
  1197.                 }
  1198.                                         // Player is smokin' bones!
  1199.                 thePlayer.mode = kFalling;
  1200.                 if (thePlayer.facingRight)
  1201.                     thePlayer.srcNum = 8;
  1202.                 else
  1203.                     thePlayer.srcNum = 9;
  1204.                 thePlayer.dest.bottom = thePlayer.dest.top + 37;
  1205.                 PlayExternalSound(kBoom2Sound, kBoom2Priority);
  1206.             }
  1207.             else                        // If the eye was "blinking", IT was killed!
  1208.             {                            // Player killed the eye!
  1209.                 if (lightningCount == 0)
  1210.                 {                        // Strike the eye with lightning!
  1211.                     lightH = theEye.dest.left + 24;
  1212.                     lightV = theEye.dest.top + 16;
  1213.                                         // Hit 'er with 15 bolts!
  1214.                     lightningCount = 15;
  1215.                 }
  1216.                 theScore += 2000L;        // A big 2000 points for killing the eye!
  1217.                 UpdateScoreNumbers();    // Refresh score display.
  1218.                 PlayExternalSound(kBonusSound, kBonusPriority);
  1219.                 
  1220.                 KillOffEye();            // Slay eye!
  1221.             }                            // Hey, anyone remember that giant eye from…
  1222.         }                                // Johnny Socko and his Flying Robot?
  1223.     }                                    // As a kid, I thought that was cool!
  1224.     else if (theEye.frame > 0)            // Eye has not yet appeared, but waits, lurking!
  1225.     {
  1226.         theEye.frame--;                    // Decrement eye timer.
  1227.         if (theEye.frame <= 0)            // When timer hits zero, eye appears!
  1228.         {
  1229.             theEye.mode = kStalking;    // The eye is after the player!
  1230.             if (lightningCount == 0)    // Strike lightning at eye!
  1231.             {
  1232.                 lightH = theEye.dest.left + 24;
  1233.                 lightV = theEye.dest.top + 16;
  1234.                 lightningCount = 6;
  1235.             }
  1236.             theEye.srcNum = 3;
  1237.             theEye.opening = 1;
  1238.             theEye.entering = TRUE;
  1239.         }
  1240.     }    
  1241. }
  1242.  
  1243. //--------------------------------------------------------------  ResolveEnemyPlayerHit
  1244.  
  1245. // Okay, a bounds test determined that the player and an enemy have collided.
  1246. // This function looks at the two and determines who wins or if it's a draw.
  1247.  
  1248. void ResolveEnemyPlayerHit (short i)
  1249. {
  1250.     short        wasVel, diff, h, v;
  1251.     
  1252.     if ((theEnemies[i].mode == kFalling) || (theEnemies[i].mode == kEggTimer))
  1253.     {                        // Okay, if the enemy is an egg…
  1254.         deadEnemies++;        // simple - the enemy dies.
  1255.         
  1256.         theEnemies[i].mode = kDeadAndGone;
  1257.         theScore += 500L;    // Add that to our score!
  1258.         UpdateScoreNumbers();
  1259.         PlayExternalSound(kBonusSound, kBonusPriority);
  1260.         InitEnemy(i, TRUE);    // Reset the enemy (I guess you could say they're reincarnated.
  1261.     }
  1262.     else                    // Now, here's a real, live sphinx enemy.
  1263.     {                        // Get their difference in altitude.
  1264.         diff = (theEnemies[i].dest.top + 25) - (thePlayer.dest.top + 19);
  1265.         
  1266.         if (diff < -2)        // Player is bested.  :(
  1267.         {                    // Strike player with lightning.
  1268.             if (lightningCount == 0)
  1269.             {
  1270.                 lightH = thePlayer.dest.left + 24;
  1271.                 lightV = thePlayer.dest.bottom - 24;
  1272.                 lightningCount = 6;
  1273.             }
  1274.                             // Player is bones.
  1275.             thePlayer.mode = kFalling;
  1276.             if (thePlayer.facingRight)
  1277.                 thePlayer.srcNum = 8;
  1278.             else
  1279.                 thePlayer.srcNum = 9;
  1280.             thePlayer.dest.bottom = thePlayer.dest.top + 37;
  1281.             PlayExternalSound(kBoom2Sound, kBoom2Priority);
  1282.         }
  1283.         else if (diff > 2)    // Yes!  Enemy is killed!
  1284.         {                    // Well ... we can't kill an enemy who is spawning.
  1285.             if ((theEnemies[i].mode == kSpawning) && (theEnemies[i].frame < 16))
  1286.                 return;
  1287.                             // Resize enemy bounds (use an egg bounds).
  1288.             h = (theEnemies[i].dest.left + theEnemies[i].dest.right) >> 1;
  1289.             if (theEnemies[i].mode == kSpawning)
  1290.                 v = theEnemies[i].dest.bottom - 2;
  1291.             else
  1292.                 v = (theEnemies[i].dest.top + theEnemies[i].dest.bottom) >> 1;
  1293.             theEnemies[i].dest.left = h - 12;
  1294.             theEnemies[i].dest.right = h + 12;
  1295.             if (theEnemies[i].mode == kSpawning)
  1296.                 theEnemies[i].dest.top = v - 24;
  1297.             else
  1298.                 theEnemies[i].dest.top = v - 12;
  1299.             theEnemies[i].dest.bottom = theEnemies[i].dest.top + 24;
  1300.             theEnemies[i].h = theEnemies[i].dest.left << 4;
  1301.             theEnemies[i].v = theEnemies[i].dest.top << 4;
  1302.                             // Enemy is a falling egg!
  1303.             theEnemies[i].mode = kFalling;
  1304.             theEnemies[i].wasDest = theEnemies[i].dest;
  1305.             theEnemies[i].wasH = theEnemies[i].h;
  1306.             theEnemies[i].wasV = theEnemies[i].v;
  1307.                             // Give player points based on enemy kind.
  1308.             switch (theEnemies[i].kind)
  1309.             {
  1310.                 case kOwl:
  1311.                 theScore += 500L;
  1312.                 break;
  1313.                 
  1314.                 case kWolf:
  1315.                 theScore += 1000L;
  1316.                 break;
  1317.                 
  1318.                 case kJackal:
  1319.                 theScore += 1500L;
  1320.                 break;
  1321.             }
  1322.             UpdateScoreNumbers();
  1323.             PlayExternalSound(kBoom2Sound, kBoom2Priority);
  1324.         }
  1325.         else        // Rare case - neither the player nor the enemy get killed.
  1326.         {            // They'll bounce off one another.
  1327.             if (theEnemies[i].hVel > 0)
  1328.                 theEnemies[i].facingRight = TRUE;
  1329.             else
  1330.                 theEnemies[i].facingRight = FALSE;
  1331.             PlayExternalSound(kScreechSound, kScreechPriority);
  1332.         }
  1333.         
  1334.         wasVel = thePlayer.hVel;
  1335.         thePlayer.hVel = theEnemies[i].hVel;
  1336.         theEnemies[i].hVel = wasVel;
  1337.         wasVel = thePlayer.vVel;
  1338.         thePlayer.vVel = theEnemies[i].vVel;
  1339.         theEnemies[i].vVel = wasVel;
  1340.     }
  1341. }
  1342.  
  1343. //--------------------------------------------------------------  CheckPlayerEnemyCollision
  1344.  
  1345. // This is a simple "bounds test" for determining player/enemy collisions.
  1346.  
  1347. void CheckPlayerEnemyCollision (void)
  1348. {
  1349.     Rect        whoCares, playTest, wrapTest;
  1350.     short        i;
  1351.     
  1352.     playTest = thePlayer.dest;        // Make a copy of player's bounds.
  1353.     InsetRect(&playTest, 8, 8);        // Shrink it by 8 pixels all 'round.
  1354.     if (thePlayer.wrapping)            // Need to test 2 players if "wraparounding".
  1355.         wrapTest = thePlayer.wrap;
  1356.     InsetRect(&wrapTest, 8, 8);
  1357.                                     // Test all enemies.
  1358.     for (i = 0; i < numEnemies; i++)
  1359.     {                                // Ignore non-existant enemies.
  1360.         if ((theEnemies[i].mode != kIdle) && (theEnemies[i].mode != kDeadAndGone))
  1361.         {                            // Simple bounds test.
  1362.             if (SectRect(&playTest, &theEnemies[i].dest, &whoCares))
  1363.             {                        // Call function to determine who wins (or tie).
  1364.                 ResolveEnemyPlayerHit(i);
  1365.             }                        // If "wrap-arounding", test other rect.
  1366.             else if (thePlayer.wrapping)
  1367.             {
  1368.                 if (SectRect(&wrapTest, &theEnemies[i].dest, &whoCares))
  1369.                     ResolveEnemyPlayerHit(i);
  1370.             }
  1371.         }
  1372.     }
  1373. }
  1374.  
  1375.